package org.grayrabbit.cms.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.grayrabbit.cms.utils.zip.FolderArchiver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opensymphony.util.TextUtils;

public class FileUtils {
	private static final Logger log = LoggerFactory.getLogger(FileUtils.class);
	  private static final int DEFAULT_BUFFER_SIZE = 4096;

	  public static int copy(InputStream input, OutputStream output)
	    throws IOException
	  {
	    return copy(input, output, DEFAULT_BUFFER_SIZE);
	  }

	  public static int copy(InputStream input, OutputStream output, int bufferSize)
	    throws IOException
	  {
	    byte[] buffer = new byte[bufferSize];
	    int count = 0;
	    int n = 0;
	    while (-1 != (n = input.read(buffer)))
	    {
	      output.write(buffer, 0, n);
	      count += n;
	    }
	    return count;
	  }

	  public static void shutdownStream(OutputStream output)
	  {
	    if (output == null)
	    {
	      return;
	    }
	    try
	    {
	      output.close();
	    }
	    catch (IOException ioe)
	    {
	    }
	  }

	  public static void shutdownStream(InputStream input)
	  {
	    if (input == null)
	    {
	      return;
	    }
	    try
	    {
	      input.close();
	    }
	    catch (IOException ioe)
	    {
	    }
	  }

	  public static boolean deleteDir(File dir)
	  {
	    if (dir == null)
	    {
	      return false;
	    }

	    File candir;
	    try
	    {
	      candir = dir.getCanonicalFile();
	    }
	    catch (IOException e)
	    {
	      return false;
	    }

	    if (!candir.equals(dir.getAbsoluteFile()))
	    {
	      return false;
	    }

	    File[] files = candir.listFiles();
	    if (files != null)
	    {
	      for (int i = 0; i < files.length; i++)
	      {
	        File file = files[i];
	        boolean deleted = !file.delete();
	        if (!deleted)
	        {
	          continue;
	        }
	        if (!file.isDirectory()) 
	        	continue; 
	        deleteDir(file);
	      }
	    }
	    return dir.delete();
	  }

	  public static void recursiveDelete(File file)
	  {
	    File[] files = null;

	    if (file.isDirectory()) {
	      files = file.listFiles();
	    }
	    else {
	      file.delete();
	      return;
	    }

	    for (int i = 0; i < files.length; i++)
	    {
	      File next = files[i];
	      recursiveDelete(next);
	    }

	    file.delete();
	  }

	  public static String getResourceContent(String resource)
	  {
	    InputStream is = ClassLoaderUtils.getResourceAsStream(resource, FileUtils.class);
	    return getInputStreamTextContent(is);
	  }

	  public static String getResourceContent(HttpServletRequest req, String resource)
	  {
	    InputStream is = req.getSession().getServletContext().getResourceAsStream(resource);
	    String result = getInputStreamTextContent(is);
	    return result==null?"":result;
	  }

	  public static String getInputStreamTextContent(InputStream is)
	  {
	    if (is == null)
	    {
	      return null;
	    }

	    String result = null;
	    try
	    {
	      ByteArrayOutputStream baos = new ByteArrayOutputStream(is.available());
	      copy(is, baos);
	      result = new String(baos.toByteArray());
	      is.close();
	    }
	    catch (IOException e)
	    {
	      log.error("IOException reading stream: " + e, e);
	    }
	    return result;
	  }

	  public static void saveTextFile(String stringContent, File destFile)  throws IOException
	  {
	    ensureFileAndPathExist(destFile);
	    FileWriter writer = new FileWriter(destFile);
	    writer.write(stringContent);
	    writer.close();
	  }

	  public static void ensureFileAndPathExist(File file)  throws IOException
	  {
	    file.getParentFile().mkdirs();
	    file.createNewFile();
	  }

	  public static boolean moveDir(File dirName, File destDir)
	  {
	    File destParent = new File(destDir.getParent());
	    if (destDir.exists())
	    {
	      destDir.delete();
	    }
	    destParent.mkdirs();
	    return dirName.renameTo(destDir);
	  }

	  public static void createZipFile(File baseDir, File zipFile) throws Exception
	  {
	    FolderArchiver compressor = new FolderArchiver(baseDir, zipFile);
	    compressor.doArchive();
	  }

	  public static List<String> readResourcesAsList(String resource)
	  {
	    List<String> result = new ArrayList<String>();
	    try
	    {
	      InputStream is = ClassLoaderUtils.getResourceAsStream(resource, FileUtils.class);
	      BufferedReader in = new BufferedReader(new InputStreamReader(is));
	      String s;
	      while ((s = in.readLine()) != null)
	      {
	        String niceS = TextUtils.noNull(s).trim();
	        if ((TextUtils.stringSet(niceS)) && (niceS.charAt(0) != '#'))
	        {
	          result.add(s);
	        }
	      }
	      is.close();
	    }
	    catch (IOException e)
	    {
	      log.error("IOException reading stream: " + e, e);
	    }

	    return result;
	  }

	  public static void copyDirectory(File srcDir, File destDir)
	    throws IOException
	  {
	    copyDirectory(srcDir, destDir, false);
	  }

	  public static void copyDirectory(File srcDir, File destDir, boolean overwrite) throws IOException
	  {
	    File[] files = srcDir.listFiles();

	    if (!destDir.exists())
	      destDir.mkdirs();
	    else {
	      log.debug(destDir.getAbsolutePath() + " already exists");
	    }
	    if (files != null)
	    {
	      for (int i = 0; i < files.length; i++)
	      {
	        File file = files[i];
	        File dest = new File(destDir, file.getName());

	        if (file.isFile())
	          copyFile(new FileInputStream(file), dest, overwrite);
	        else
	          copyDirectory(file, dest, overwrite);
	      }
	    }
	  }

	  public static void copyFile(File srcFile, File destFile)
	    throws IOException
	  {
	    copyFile(srcFile, destFile, true);
	  }

	  public static void copyFile(File srcFile, File destFile, boolean overwrite) throws IOException
	  {
	    if (!srcFile.exists())
	    {
	      String message = "File " + srcFile + " does not exist";
	      throw new IOException(message);
	    }

	    InputStream input = new FileInputStream(srcFile);
	    copyFile(input, destFile, overwrite);

	    if (srcFile.length() != srcFile.length())
	    {
	      String message = "Failed to copy full contents from " + srcFile + " to " + destFile;
	      throw new IOException(message);
	    }
	  }

	  public static void copyFile(InputStream srcStream, File destFile) throws IOException
	  {
	    copyFile(srcStream, destFile, false);
	  }

	  public static void copyFile(InputStream srcStream, File destFile, boolean overwrite) throws IOException
	  {
	    File parentFile = destFile.getParentFile();
	    if (!parentFile.isDirectory())
	    {
	      parentFile.mkdirs();
	    }

	    if (destFile.exists())
	    {
	      if (!destFile.canWrite())
	      {
	        String message = "Unable to open file " + destFile + " for writing.";
	        throw new IOException(message);
	      }

	      if (overwrite)
	      {
	        log.debug("Overwriting file at: " + destFile.getAbsolutePath());
	        writeStreamToFile(srcStream, destFile);
	      }
	      else
	      {
	        shutdownStream(srcStream);
	        log.warn(destFile.getAbsolutePath() + " already exists");
	      }
	    }
	    else
	    {
	      destFile.createNewFile();
	      writeStreamToFile(srcStream, destFile);
	    }
	  }

	  private static void writeStreamToFile(InputStream srcStream, File destFile) throws IOException
	  {
	    InputStream input = null;
	    OutputStream output = null;
	    try
	    {
	      input = new BufferedInputStream(srcStream);
	      output = new BufferedOutputStream(new FileOutputStream(destFile));
	      int ch;
	      while ((ch = input.read()) != -1)
	        output.write(ch);
	    }
	    catch (IOException e)
	    {
	      log.error("Error writing stream to file: " + destFile.getAbsolutePath());
	      throw e;
	    }
	    finally
	    {
	      shutdownStream(input);
	      shutdownStream(output);
	    }
	  }
}
